package com.dragon.repository.config.aop;

import cn.hutool.core.util.StrUtil;
import com.alibaba.ttl.TransmittableThreadLocal;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.handler.DataPermissionHandler;
import com.dragon.common.util.JwtUtil;
import com.dragon.repository.config.annotation.DataScope;
import com.dragon.repository.entity.*;
import com.dragon.repository.mapper.AdminDepartmentMapper;
import com.dragon.repository.mapper.DepartmentMapper;
import com.dragon.repository.mapper.RoleDepartmentMapper;
import com.dragon.repository.mapper.RoleMapper;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.HexValue;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Aspect
@Slf4j
@Component
public class DataScopePermissionHandler implements DataPermissionHandler {

    /**
     * 通过ThreadLocal记录权限相关的属性值
     */
    private static final ThreadLocal<DataScopeParam> threadLocal = new TransmittableThreadLocal<>();


    @Autowired
    RoleMapper roleMapper;

    @Autowired
    DepartmentMapper departmentMapper;

    @Autowired
    RoleDepartmentMapper roleDepartmentMapper;

    @Autowired
    AdminDepartmentMapper adminDepartmentMapper;


    /**
     * 清空当前线程上次保存的权限信息
     */
    @After("dataScopePointCut()")
    public void clearThreadLocal(){
        threadLocal.remove();
        log.debug("threadLocal.remove()");
    }

    /**
     * 注解对象
     * 该成员变量在并发情况下导致多个线程公用了一套 @DataScope 配置的参数，导致sql拼接失败，必须改为局部变量
     * 修改于：2022.04.28
     */
//    private DataScope controllerDataScope;

    /**
     * 配置织入点
     */
    @Pointcut("@annotation(com.dragon.repository.config.annotation.DataScope)")
    public void dataScopePointCut() {

    }

    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) {
        // 获得注解
        DataScope controllerDataScope = getAnnotationLog(point);
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        assert sra != null;
        HttpServletRequest request = sra.getRequest();
        if (controllerDataScope != null && JwtUtil.getIsAdmin(request)==2) {
//            String[] requestUri = request.getRequestURI().split("/");
            DataScopeParam dataScopeParam = new DataScopeParam();
            dataScopeParam.setAdminId(JwtUtil.getAdminId(request));
            dataScopeParam.setIsAdmin(JwtUtil.getIsAdmin(request));
//            dataScopeParam.setAuthCode(JwtUtil.getAuthCode(request));
//            dataScopeParam.setDepartmentId(JwtUtil.getDepartmentIds(request));
            dataScopeParam.setDeptField(controllerDataScope.deptField());
            dataScopeParam.setAuthCodeString(controllerDataScope.authCodeString());
            dataScopeParam.setDeptAlias(controllerDataScope.deptAlias());
            dataScopeParam.setAdminAlias(controllerDataScope.adminAlias());
            dataScopeParam.setAdminField(controllerDataScope.adminField());
            dataScopeParam.setIsCheckAdmin(controllerDataScope.isCheckAdmin());
            dataScopeParam.setIsCheckDept(controllerDataScope.isCheckDept());
            if(dataScopeParam.getIsCheckAdmin() == true && dataScopeParam.getIsCheckDept() == true){
                Role role = roleMapper.selectAuthDataCode(Integer.valueOf(dataScopeParam.getAdminId()),dataScopeParam.getAuthCodeString());
                Integer authDataCode = role.getDataAuthType();
                dataScopeParam.setAuthCode(authDataCode);
                if(authDataCode == 2 || authDataCode == 3 || authDataCode == 4){
                    //查询所在部门
                    QueryWrapper<AdminDepartment> departmentQueryWrapper = new QueryWrapper<>();
                    departmentQueryWrapper.lambda().eq(AdminDepartment::getAdminId, Integer.valueOf(dataScopeParam.getAdminId()));
                    AdminDepartment adminDepartment = adminDepartmentMapper.selectOne(departmentQueryWrapper);
                    String departmentIds = String.valueOf(adminDepartment.getDepartmentId());
                    if (authDataCode == 3) {
                        //获取下级所有部门
                        List<Department> departments = departmentMapper.selectDepartmentListByAdminId(adminDepartment.getDepartmentId());
                        List<Integer> integers = departments.stream().map(Department::getId).collect(Collectors.toList());
                        departmentIds = integers.toString();
                    }
                    if (authDataCode == 4) {
                        QueryWrapper<RoleDepartment> roleDepartmentQueryWrapper = new QueryWrapper<>();
                        roleDepartmentQueryWrapper.lambda().eq(RoleDepartment::getRoleId, Integer.valueOf(dataScopeParam.getAdminId()));
                        List<RoleDepartment> departments = roleDepartmentMapper.selectList(roleDepartmentQueryWrapper);
                        List<Integer> integers = departments.stream().map(RoleDepartment::getDepartmentId).collect(Collectors.toList());
                        departmentIds = integers.toString();
                    }
                    if(StrUtil.isNotBlank(departmentIds) && !departmentIds.equals("")){
                        dataScopeParam.setDepartmentId(departmentIds);
                    }
                }
            }
            threadLocal.set(dataScopeParam);
            log.debug("currentUser.getDataScope() = {}", dataScopeParam);
            // 获取当前的用户及相关属性，需提前获取和保存数据权限对应的部门ID集合
//            User currentUser = SecurityUtil.getUser();
//            DataScopeParam dataScopeParam = new DataScopeParam(controllerDataScope.deptAlias(),
//                    controllerDataScope.deptField(),
//                    currentUser.isAdmin(),
//                    currentUser.getDataScope());
//            threadLocal.set(dataScopeParam);
//            log.debug("currentUser.getDataScope() = {}", currentUser.getDataScope());
        }
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }

    /**
     * @param where             原SQL Where 条件表达式
     * @param mappedStatementId Mapper接口方法ID
     * @return
     */
    @SneakyThrows
    @Override
    public Expression getSqlSegment(Expression where, String mappedStatementId) {
        DataScopeParam dataScopeParam = threadLocal.get();
        //判断是否是管理员，获取可见数据权限为全部
        if(dataScopeParam == null){
            return where;
        }
//        Expression wheres = null;
        // 同时验证人和部门
        String sql = "";
        if(dataScopeParam.getIsCheckAdmin() == true && dataScopeParam.getIsCheckDept() == true){
            //查询当前访问接口的用户关联的角色以及菜单id,获取dataAuchCod
            //全部可见
            if(dataScopeParam.getAuthCode() == 0){
                return where;
            }
            if (where == null) {
                where = new HexValue(" 1 = 1 ");
            }
            // 自己可见
            if(dataScopeParam.getAuthCode() == 1 && !dataScopeParam.getAdminField().equals("")){
                List<String> stringList = Arrays.asList(dataScopeParam.adminField.split(","));
                if(stringList.size()==1){
                    // 字段
                    String adminSql = "".equals(dataScopeParam.adminAlias) ? dataScopeParam.adminField : dataScopeParam.adminAlias + "." + dataScopeParam.adminField;
                    EqualsTo equalsto = new EqualsTo(new Column(adminSql),new StringValue(dataScopeParam.getAdminId()));
//                    wheres = new AndExpression(wheres,equalsto);
                    sql = sql.concat(" ").concat(equalsto.toString());
//                    where = new AndExpression(where,equalsto);
                } else {
                    for (int i = 0; i < stringList.size(); i++) {
                        if(i == 0){
                            // 字段
                            String adminSql = "".equals(dataScopeParam.adminAlias) ? stringList.get(i) : dataScopeParam.adminAlias + "." + stringList.get(i);
                            EqualsTo equalsto = new EqualsTo(new Column(adminSql),new StringValue(dataScopeParam.getAdminId()));
//                            wheres = new AndExpression(wheres,equalsto);
                            sql = sql.concat(" ").concat(equalsto.toString());
                            //where = new AndExpression(where,equalsto);
                        }
                        if(i > 0){
                            // 字段
                            String adminSql = "".equals(dataScopeParam.adminAlias) ? stringList.get(i) : dataScopeParam.adminAlias + "." + stringList.get(i);
                            EqualsTo equalsto = new EqualsTo(new Column(adminSql),new StringValue(dataScopeParam.getAdminId()));
//                            wheres = new OrExpression(wheres,equalsto);
//                            where = new OrExpression(where,equalsto);
                            sql = sql.concat( " ").concat(" or ").concat(equalsto.toString());
                        }
                    }
                }

            }

            //本部门可见||本部门及以下部门可见||选择部门可见
            if(dataScopeParam.getAuthCode() == 2 || dataScopeParam.getAuthCode() == 3 || dataScopeParam.getAuthCode() == 4){
                List<String> stringDeptList = Arrays.asList(dataScopeParam.deptField.split(","));
                if(stringDeptList.size() == 1){
                    //把集合转变为JSQLParser需要的元素列表
                    ItemsList itemsList;
                    if(StrUtil.isNotBlank(dataScopeParam.getDepartmentId()) && !dataScopeParam.getDepartmentId().equals("")){
                        String deptSql = "".equals(dataScopeParam.deptAlias) ? dataScopeParam.deptField : dataScopeParam.deptAlias + "." + dataScopeParam.deptField;
                        //查看权限内的数据
                        String[] departments = dataScopeParam.getDepartmentId().split(",");
                        List<String> depart  =  Arrays.asList(departments);
                        itemsList = new ExpressionList(depart.stream().map(StringValue::new).collect(Collectors.toList()));
                        InExpression inExpression = new InExpression(new Column(deptSql), itemsList);
//                        where = new AndExpression(where, inExpression);
//                        wheres = new AndExpression(wheres, inExpression);
                        sql = sql.concat(" ").concat(inExpression.toString().replace("[","").replace("]",""));
                    }
                } else {
                    for (int i = 0; i < stringDeptList.size(); i++) {
                        if(i==0){
                            //把集合转变为JSQLParser需要的元素列表
                            ItemsList itemsList;
                            if(StrUtil.isNotBlank(dataScopeParam.getDepartmentId()) && !dataScopeParam.getDepartmentId().equals("")){
                                String deptSql = "".equals(dataScopeParam.deptAlias) ? stringDeptList.get(i) : dataScopeParam.deptAlias + "." + stringDeptList.get(i);
                                //查看权限内的数据
                                String[] departments = dataScopeParam.getDepartmentId().split(",");
                                List<String> depart  =  Arrays.asList(departments);
                                itemsList = new ExpressionList(depart.stream().map(StringValue::new).collect(Collectors.toList()));
                                InExpression inExpression = new InExpression(new Column(deptSql), itemsList);
//                                where =  new AndExpression(where, inExpression);
//                                wheres =  new AndExpression(wheres, inExpression);
                                sql = sql.concat(" ").concat(inExpression.toString().replace("[","").replace("]",""));
                            }
                        }

                        if(i>0){
                            //把集合转变为JSQLParser需要的元素列表
                            ItemsList itemsList;
                            if(StrUtil.isNotBlank(dataScopeParam.getDepartmentId()) && !dataScopeParam.getDepartmentId().equals("")){
                                String deptSql = "".equals(dataScopeParam.deptAlias) ? stringDeptList.get(i) : dataScopeParam.deptAlias + "." + stringDeptList.get(i);
                                //查看权限内的数据
                                String[] departments = dataScopeParam.getDepartmentId().split(",");
                                List<String> depart  =  Arrays.asList(departments);
                                itemsList = new ExpressionList(depart.stream().map(StringValue::new).collect(Collectors.toList()));
                                InExpression inExpression = new InExpression(new Column(deptSql), itemsList);
//                                where = new OrExpression(where, inExpression);
//                                wheres = new OrExpression(wheres, inExpression);
                                sql = sql.concat("").concat(" or ").concat(inExpression.toString().replace("[","").replace("]",""));
                            }
                        }
                    }
                }
            }
        }


        // 只验证人，例如说日报，只能看见自己的以及汇报给自己的，验证多个字段
        if(dataScopeParam.getIsCheckAdmin() == true && dataScopeParam.getIsCheckDept() == false){
            List<String> stringList = Arrays.asList(dataScopeParam.adminField.split(","));
            if(stringList.size()==1){
                // 字段
                String adminSql = "".equals(dataScopeParam.adminAlias) ? dataScopeParam.adminField : dataScopeParam.adminAlias + "." + dataScopeParam.adminField;
                EqualsTo equalsto = new EqualsTo(new Column(adminSql),new StringValue(dataScopeParam.getAdminId()));
//                sql.concat("").concat(equalsto.toString());
                return new AndExpression(where,equalsto);
            } else {
                for (int i = 0; i < stringList.size(); i++) {
                    if(i == 0){
                        // 字段
                        String adminSql = "".equals(dataScopeParam.adminAlias) ? stringList.get(i) : dataScopeParam.adminAlias + "." + stringList.get(i);
                        EqualsTo equalsto = new EqualsTo(new Column(adminSql),new StringValue(dataScopeParam.getAdminId()));
//                        where = new AndExpression(where,equalsto);
//                        wheres = new AndExpression(wheres,equalsto);
                        sql = sql.concat("").concat(equalsto.toString());
                    }
                    if(i > 0){
                        // 字段
                        String adminSql = "".equals(dataScopeParam.adminAlias) ? stringList.get(i) : dataScopeParam.adminAlias + "." + stringList.get(i);
                        EqualsTo equalsto = new EqualsTo(new Column(adminSql),new StringValue(dataScopeParam.getAdminId()));
//                        where = new OrExpression(where,equalsto);
//                        wheres = new OrExpression(wheres,equalsto);
                        sql = sql.concat("").concat(" or ").concat(equalsto.toString());
                    }
                }
            }
        }
        sql = "(".concat(sql).concat(")");
        if(sql.equals("()")){
           return where;
        }
        where = new AndExpression(where,new HexValue(sql));
        return where;
    }

    /**
     * ThreadLocal存储对象
     */
    @Data
    static class DataScopeParam{
        /**
         * 部门表的别名
         */
        private String deptAlias;

        /**
         * 部门字段名
         */
        private String deptField;

        /**
         * 部门id集合
         */
        private String departmentId;

        /**
         * 用户表别名
         */
        private String adminField;

        /**
         * 用户字段名
         */
        private String adminId;

        /**
         * 用户表别名
         */
        private String adminAlias;

        /**
         * 数据权限类别
         */
        private Integer authCode;

        /**
         * 访问权限字符串
         */
        private String authCodeString;

        /**
         * 是否是管理员
         */
        private Integer isAdmin;

        /**
         * 是否验证部门
         */
        private Boolean isCheckDept;

        /**
         * 是否验证人
         */
        private Boolean isCheckAdmin;
    }
}
